"""
sqlalchemy 官方文档
https://docs.sqlalchemy.org.cn/en/20/index.html
https://docs.sqlalchemy.org.cn/en/20/orm/extensions/asyncio.html
"""
from contextlib import asynccontextmanager

from sqlalchemy import Engine
from sqlalchemy.event import remove, listens_for
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from app.core.config import settings
from app.core.log import logger
from app.models.base_model import SuperEntityMixin, EntityMixin, update_fill, insert_fill


class DatabaseConfiguration:
    """数据库配置"""

    def __init__(self):
        # 创建异步引擎
        self._engine = create_async_engine(
            str(settings.database_url),  # url
            echo=False,  # 是否开启SQL日志
            pool_size=settings.database_pool_size,  # 连接池大小
        )
        # 创建异步会话工厂
        self._async_session_factory = async_sessionmaker(
            bind=self._engine,  # 绑定异步引擎
            expire_on_commit=False,  # 提交后保留对象状态，避免隐式查询
            autoflush=False,  # 关闭自动刷新以提高批量操作性能
            class_=AsyncSession,  # 显式指定异步会话类（可选，默认已包含）
        )

    def get_async_session_factory(self) -> async_sessionmaker[AsyncSession]:
        """获取异步会话工厂"""
        return self._async_session_factory

    async def disconnect(self):
        """断开数据库连接"""
        await self._engine.dispose()


# 创建数据库对象
_db = DatabaseConfiguration()
# 创建会话管理器
async_session_factory = _db.get_async_session_factory()


@asynccontextmanager
async def get_session():
    """ 获取数据库会话 """
    async with async_session_factory() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()


async def disconnect_db():
    """断开数据库连接"""
    from sqlalchemy import log
    await _db.disconnect()
    logger.info("数据库 断开连接成功")


@listens_for(Engine, "before_cursor_execute")
def before_cursor_execute(conn, cursor, statement: str, parameters, context, executemany):
    # 如果关闭sql日志打印
    if not settings.database_log:
        return

    # 多行SQL转成单行
    one_line_sql = statement.replace('\n', '')
    logger.debug("==> Preparing : {}", one_line_sql)

    # 如果sql有参数
    if parameters:
        logger.debug("==> Parameters: {}", parameters)


def remove_listener():
    """
    移除事件监听器函数

    用于在适当的时候移除已注册的事件监听器，防止内存泄漏或重复监听
    :return: None
    """
    remove(Engine, "before_cursor_execute", before_cursor_execute)
    remove(SuperEntityMixin, "before_insert", insert_fill)
    remove(EntityMixin, "before_update", update_fill)
